package com.bailiban.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.bailiban.common.ConnUtil;
import com.bailiban.common.DemoException;
import com.bailiban.common.TokenHolder;
import com.bailiban.entity.News;
import com.bailiban.repository.NewsRepository;
import com.bailiban.service.NewsService;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ClassUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author zhulang
 * @Classname NewsServiceImpl
 * @Description TODO
 * @Date 2020/4/5 18:20
 */
@Service
public class NewsServiceImpl implements NewsService {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Autowired
    private NewsRepository newsRepository;
    //  填写网页上申请的appkey 如 $apiKey="g8eBUMSokVB1BHGmgxxxxxx"
    @Value("${appKey}")
    private String appKey;

    // 填写网页上申请的APP SECRET 如 $secretKey="94dc99566550d87f8fa8ece112xxxxx"
    @Value("${secretKey}")
    private String secretKey;
    // 发音人选择, 基础音库：0为度小美，1为度小宇，3为度逍遥，4为度丫丫，
    // 精品音库：5为度小娇，103为度米朵，106为度博文，110为度小童，111为度小萌，默认为度小美
    @Value("${per}")
    private int per;
    // 语速，取值0-15，默认为5中语速
    @Value("${spd}")
    private int spd;
    // 音调，取值0-15，默认为5中语调
    @Value("${pit}")
    private int pit;
    // 音量，取值0-9，默认为5中音量
    @Value("${vol}")
    private int vol;

    // 下载的文件格式, 3：mp3(default) 4： pcm-16k 5： pcm-8k 6. wav
    @Value("${aue}")
    private int aue;
    @Value("${url}")
    public String url;
    // 可以使用https
    @Value("${cuid}")
    private String cuid;

    @Override
    public Page<News> findAll(Integer page, Integer size) {
//        根据时间戳进行排序
        Sort sort = Sort.by(Sort.Order.desc("timestamp"));
//        进行分页设置
        Pageable pageable = PageRequest.of(page, size, sort);
        Page<News> newsPage = newsRepository.findAll(pageable);
        return newsPage;
    }

    @Override
    public News findLatestNews(Integer size) {
        //需要从redis中查询日期，判断这个日期是不是今天的时间
//        如果这个日期不是今天的日期，那么就直接查询数据库，然后存放到redis中。并且还更新redis中的日期
        String newsDate = redisTemplate.boundValueOps("newsDate").get();
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        String today = simpleDateFormat.format(new Date());
        List<News> latestNews;

        News news = new News();
//        查询redis中的news数据
        String latestNew1 = redisTemplate.boundValueOps("latestNew").get();
        if (!today.equals(newsDate) || StringUtils.isEmpty(latestNew1)) {
//日期不匹配，从数据库查询
            latestNews = newsRepository.findLatestNews(size);
//             为了满足后面的ai语音播报的功能，我们仅仅需要一条新闻即可，即把指定条目的新闻内容content全部放到一条新闻中
            StringBuilder builder = new StringBuilder("大家好，我是小溥，今天给大家带来一下新闻信息");
            for (News latestNew : latestNews) {
                builder.append(latestNew.getContent());
            }
            builder.append("谢谢大家的收听，我们下次再见！");
            String allContent = builder.toString();
//            调用初始化方法获取音频url
            //创建一个线程池
            ExecutorService threadPool2 = Executors.newCachedThreadPool();
            Integer finalSize = size;
            FutureTask<String> future = new FutureTask<String>(new Callable<String>() {
                @Override
                public String call() throws Exception {
                    String initUrl;
                    try {
                        System.out.println("音频文件生成异步任务开始执行....");
                        initUrl = init(allContent);
//                        暂停3秒钟用于生产音频文件
                        Thread.sleep(3000);
                    } catch (Exception e) {
                        return "";
                    }
                    return initUrl;
                }

            });
//            执行音频生成任务
            threadPool2.execute(future);
            boolean flag = true;
            while (flag) {
                //异步任务完成并且未被取消，并且音频文件生成成功，则不再进入此循环
                try {
                    String file = ClassUtils.getDefaultClassLoader().getResource("static/audio/result.wav").getFile();
                    if (future.isDone() && !future.isCancelled()) {
                        String futureUrl = null;
                        try {
                            futureUrl = future.get();
                            news.setUrl(futureUrl);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } catch (ExecutionException e) {
                            e.printStackTrace();
                        }
                        assert futureUrl != null;
                        System.out.println("====音频异步任务返回的结果是：" + futureUrl);
                        flag = false;
                    }
                } catch (Exception e) {
//                    文件不存在就继续暂缓2秒钟用于生产文件
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException ex) {
                        ex.printStackTrace();
                    }
                    flag = true;
                }
            }
            //关闭线程池
            if (!threadPool2.isShutdown()) {
                threadPool2.shutdown();
            }
            news.setTitle("今日疫情播报");
            news.setTimestamp(new Date());
            news.setSource("小溥新闻");
            news.setContent(allContent);
            System.out.println("新闻内容：" + allContent);
            ObjectMapper objectMapper = new ObjectMapper();
            String redisNews = null;
            try {
                redisNews = objectMapper.writeValueAsString(news);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
//           把获取的新闻存入redis中
            redisTemplate.boundValueOps("latestNew").set(redisNews);
//            把今天的日期存入redis中
            redisTemplate.boundValueOps("newsDate").set(today);
        } else {
            news = JSONObject.parseObject(latestNew1, News.class);
        }

        return news;
    }

    //    获取音频文件的url
    private String init(String content) throws IOException, DemoException {
        TokenHolder holder = new TokenHolder(appKey, secretKey, TokenHolder.ASR_SCOPE);
        holder.refresh();
        String token = holder.getToken();

        // 此处2次urlencode， 确保特殊字符被正确编码
        String params = "tex=" + ConnUtil.urlEncode(ConnUtil.urlEncode(content));
        params += "&per=" + per;
        params += "&spd=" + spd;
        params += "&pit=" + pit;
        params += "&vol=" + vol;
        params += "&cuid=" + cuid;
        params += "&tok=" + token;
        params += "&aue=" + aue;
        params += "&lan=zh&ctp=1";
//        System.out.println(url + "?" + params); // 反馈请带上此url，浏览器上可以测试

        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setConnectTimeout(5000);
        PrintWriter printWriter = new PrintWriter(conn.getOutputStream());
        printWriter.write(params);
        printWriter.close();
        String contentType = conn.getContentType();
        String downloadDir = ResourceUtils.getURL("classpath:").getPath() + "static/audio/";
        System.out.println("下载的路径：" + downloadDir);
        if (contentType.contains("audio/")) {
            byte[] bytes = ConnUtil.getResponseBytes(conn);
            String format = getFormat(aue);
            File file = new File("src/main/resources/static/audio/result." + format);
            // 打开mp3文件即可播放
            // System.out.println( file.getAbsolutePath());
            FileOutputStream os = new FileOutputStream(file);
            os.write(bytes);
            os.close();
            System.out.println("audio file write to " + file.getAbsolutePath());
        } else {
            System.err.println("ERROR: content-type= " + contentType);
            String res = ConnUtil.getResponseString(conn);
            System.err.println(res);
        }
        return url + "?" + params;
    }

    // 下载的文件格式, 3：mp3(default) 4： pcm-16k 5： pcm-8k 6. wav
    private String getFormat(int aue) {
        String[] formats = {"mp3", "pcm", "pcm", "wav"};
        return formats[aue - 3];
    }
}
